home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Utilities / Installers / Smaller Installer 2.0.1 / Preinstalled version / Examples / Hook Procedure Examples / LocateFolderHook / LocateFolderHook.c next >
Encoding:
C/C++ Source or Header  |  1996-07-26  |  20.9 KB  |  636 lines  |  [TEXT/CWIE]

  1. /******************************************************************************
  2.     Smaller Installer © 1996 Bill Goodman, All Rights Reserved
  3. *******************************************************************************
  4.  
  5. Locate Folder Hook Example
  6.  
  7. This installer hook procedure allows the user to use a dialog to select the
  8. location for an installed folder.
  9.  
  10. To build this hook procedure, compile this code and create a code resource
  11. (Type:SICR, ID:501, non-preloaded, nonpurgeable, unlocked, unprotected,
  12. non-sysheap). Add this resource to the "LocateFolderHook.rsrc" file. Copy all the
  13. resources in "LocateFolderHook.rsrc" to your installer's resource file.
  14.  
  15. Set the strings in the STR#:500 resource to the following values:
  16.     1 - Name of folder to relocate (must be unique in archive)
  17.     2 - Prompt string for install dialog
  18.     3 - Prompt string for remove dialog
  19.  
  20. *******************************************************************************
  21. Notes
  22.     o    There appears to be a bug in System 7.1 which occurs when the "Trash"
  23.         item is selected in the desktop folder. This selection may cause the
  24.         folder selection dialog to display "Trash" or the name of the current
  25.         volume.
  26.     o    If the user's system has more than 6 folders and volumes to display on
  27.         the desktop, the only way to select the desktop folder is to click on the
  28.         grayed out trash item. It is not obvious that this will select the
  29.         desktop so many users may have trouble figuring this out. Furthermore,
  30.         for older systems, the bug described above may prevent selecting the
  31.         desktop folder using this method. As a result, you should expect that the
  32.         user may NOT be able to select the desktop folder using this hook code.
  33. ******************************************************************************/
  34.  
  35. // This file is compatible with version 2.1 of the universal headers
  36. #include <Aliases.h>
  37. #include <Folders.h>
  38. #include <LowMem.h>
  39. #include <StandardFile.h>
  40. #include <TextUtils.h>
  41.  
  42. #ifdef __MWERKS__
  43. #include <A4Stuff.h>
  44. #endif
  45.  
  46. #include <SetUpA4.h>
  47.  
  48. #include "SIHookProc.h"
  49.  
  50.  
  51. /******************************************************************************
  52.     Module Internal Function Prototypes
  53. ******************************************************************************/
  54. void BeforeItemFunction(void);
  55. void BeginOperationFunction(void);
  56.  
  57. Boolean GetFolder(StringPtr prompt, short *foldVRefNum, long *foldDirID);
  58. pascal short GetFolderDlgHookProc(short item, DialogPtr theDialog);
  59. pascal Boolean GetFolderDlgFilterProc(DialogPtr theDialog, EventRecord *theEvent, short *itemHit);
  60. pascal Boolean AllFileFilterProc(CInfoPBPtr infoPtr);
  61. short TruncateLengthStr(StringPtr theStr, short maxWidth);
  62. Boolean CreateNewFolder(void);
  63.  
  64.  
  65. /******************************************************************************
  66.     Constant Definitions
  67. ******************************************************************************/
  68. // Dialog Definitions
  69. #define getFolderDlg                    500    // GetFolder dialog (modified SFGetFile)
  70. #define oldOpen_getFolderDlg        1        // OLD_OPEN button
  71. #define cancel_getFolderDlg        3        // CANCEL button
  72. #define newOpen_getFolderDlg        11        // NEW_OPEN button
  73. #define select_getFolderDlg        12        // SELECT button
  74. #define newFolder_getFolderDlg    13        // NEW_FOLDER button
  75.  
  76. #define newFolderDlg                    501    // NewFolder dialog
  77. #define ok_newFolderDlg                1        // OK button
  78. #define name_newFolderDlg            3        // Name edit text field
  79.  
  80. // Alert Definitions
  81. #define changeVolAlrt                510    // "If you wish to use a different volume, please use the DRIVE button to select the volume and try again."
  82. #define folderErrAlrt                511    // "The new folder could not be created."
  83.  
  84. // Indexed String Definitions
  85. #define genIStrID            500    // Indexed string resource ID
  86. enum
  87.     {
  88.     folderNameIStr = 1,            // Name of folder to relocate
  89.     installPromptIStr,            // Prompt string for install dialog
  90.     removePromptIStr                // Prompt string for remove dialog
  91.     };
  92.  
  93. #define getFolderIStrID    501    // Indexed string resource ID
  94. enum
  95.     {
  96.     selectBeginIStr = 1,            // Title for SELECT button "Select “"
  97.     ellipsisIStr,                    // Ellipsis string "…"
  98.     selectEndIStr,                    // End of title for SELECT button "”"
  99.     newFolderTitleIStr,            // Default name of new folder "untitled folder"
  100.     desktopIStr                        // Alternate name for desktop folder "Desktop"
  101.     };
  102.  
  103.  
  104. /******************************************************************************
  105.     Module Variable Definitions
  106. ******************************************************************************/
  107. SIHookParmBlk *gParms;                    // Global pointer to parameter block
  108. unsigned char gEmptyStr[] = "\p";    // Global empty string
  109. long gFolderDirID;                        // Directory ID of destination folder
  110.  
  111. // GetFolder variables
  112. Boolean gFolderSelected;                // True if user selected a folder
  113. short gLastSFSaveDisk;                    // Value of SFSaveDisk when selection last changed
  114. SFReply gSFReply;                            // Standard file reply record
  115.  
  116.  
  117. /*****************************************************************************/
  118. pascal void main(
  119.         SIHookParmBlk *parmBlk    // Pointer to parameter block
  120.         )
  121. /******************************************************************************
  122.     This is the main entry point for the installer hook procedure.
  123. ******************************************************************************/
  124. {
  125. #ifdef __MWERKS__
  126. long holdA4;
  127. #endif
  128.  
  129. // Set up access to global variables
  130. #ifdef THINK_C
  131. RememberA0();
  132. SetUpA4();
  133. #endif
  134.  
  135. #ifdef __MWERKS__
  136. holdA4 = SetCurrentA4();
  137. RememberA4();
  138. #endif
  139.  
  140. gParms = parmBlk;
  141.  
  142. switch (gParms->function)
  143.     {
  144.     case siHookBeginOperation:
  145.         BeginOperationFunction();
  146.         break;
  147.  
  148.     case siHookBeforeItem:
  149.         BeforeItemFunction();
  150.         break;
  151.     }
  152.  
  153. // Restore original A4 value
  154. #ifdef THINK_C
  155. RestoreA4();
  156. #endif
  157.  
  158. #ifdef __MWERKS__
  159. SetA4(holdA4);
  160. #endif
  161. }
  162.  
  163.  
  164. /*****************************************************************************/
  165. void BeginOperationFunction(void)
  166. /******************************************************************************
  167.     Input parameters:
  168.         "targetVRefNum"    Volume reference number of target volume
  169.         "groupAPFlags"        Groups currently selected
  170.         "groupQUSel"
  171.         "groupVZSel"
  172.         "group32Flags"
  173.         "group64Flags"
  174.         "group96Flags"
  175.         "groupEnvironFlags"
  176.         "passwordPtr"        Pointer to password string
  177.         "filesRemaining"    Number of files remaining to install or remove
  178.         "bytesRemaining"    Number of bytes of data remaining to install or remove
  179.         "doingRemove"        Non-zero if doing remove operation
  180.  
  181.     Returns:
  182.         "passwordPtr"        Pointer to password string
  183.         "result"                Hook result code (siHookNoErr, siHookQuit, siHookAbort)
  184.  
  185.     This function is called when the install button or the remove button is
  186.     clicked to begin installing or removing files.
  187. ******************************************************************************/
  188. {
  189. Str255 promptStr;
  190. short vRefNum;
  191.  
  192. // Display dialog prompting user to select the location of the folder
  193. LMSetSFSaveDisk(-gParms->targetVRefNum);    // Set default directory to root
  194. LMSetCurDirStore(fsRtDirID);
  195. GetIndString(promptStr, genIStrID, gParms->doingRemove ? removePromptIStr : installPromptIStr);
  196. if (promptStr[0] == 0)
  197.     goto FatalError;
  198. if (GetFolder(promptStr, &vRefNum, &gFolderDirID))
  199.     goto Abort;    // User cancelled or a serious error occurred
  200. if (vRefNum != gParms->targetVRefNum)
  201.     {    // Error - user specified a location off the target volume
  202.     CautionAlert(changeVolAlrt, NULL);
  203.     goto Abort;
  204.     }
  205. return;    // Good completion
  206.  
  207. // Abort operation
  208. Abort:
  209. gParms->result = siHookAbort;
  210. return;
  211.  
  212. // Fatal error occurred
  213. FatalError:
  214. SysBeep(1);
  215. gParms->result = siHookQuit;    // Force installer to quit
  216. }
  217.  
  218.  
  219. /*****************************************************************************/
  220. void BeforeItemFunction(void)
  221. /******************************************************************************
  222.     Input parameters:
  223.         "targetVRefNum"    Volume reference number of target volume
  224.         "groupAPFlags"        Groups currently selected
  225.         "groupQUSel"
  226.         "groupVZSel"
  227.         "group32Flags"
  228.         "group64Flags"
  229.         "group96Flags"
  230.         "groupEnvironFlags"
  231.         "filesRemaining"    Number of files remaining to install or remove
  232.         "bytesRemaining"    Number of bytes of data remaining to install or remove
  233.         "doingRemove"        Non-zero if doing remove operation
  234.         "anyItemsSkipped"    Non-zero if any item has been skipped during operation
  235.         "desVRefNum"        Volume reference number of destination volume
  236.         "desDirID"            Directory ID of destination directory
  237.         "itemName"            Name of item to install or remove
  238.         "itemIsFolder"        Non-zero if item is a folder
  239.         "fileType"            File type (files only)
  240.         "fileCreator"        File creator (files only)
  241.         "createDate"        Creation date (files only)
  242.         "lastModDate"        Last modification date (files only)
  243.         "rsrcForkLen"        Length of resource fork (files only)
  244.         "dataForkLen"        Length of data fork (files only)
  245.  
  246.     Returns:
  247.         "result"                Hook result code (siHookNoErr, siHookQuit, siHookAbort, siHookSkip, siHookItemDone)
  248.         "desDirID"            Directory ID of destination directory
  249.         "itemName"            Name of item to install or remove
  250.         "itemInfo"            Reference passed to AfterItem call
  251.  
  252.     This function is called before each item is installed or removed.
  253. ******************************************************************************/
  254. {
  255. static Str63 folderName = "\p";
  256. CInfoPBRec ib;
  257.  
  258. if (gParms->itemIsFolder)
  259.     {    // Check to see if this item is the folder to relocate
  260.     if (folderName[0] == 0)
  261.         {    // Get folder name from resource
  262.         GetIndString(folderName, genIStrID, folderNameIStr);
  263.         if (folderName[0] == 0)
  264.             goto FatalError;
  265.         }
  266.  
  267.     if (!EqualString(gParms->itemName, folderName, true, true))
  268.         return;    // Names do not match - no action required
  269.     
  270.     // Names match - change the destination directory
  271.     ib.dirInfo.ioVRefNum = gParms->targetVRefNum;
  272.     ib.dirInfo.ioDrDirID = gFolderDirID;
  273.     ib.dirInfo.ioNamePtr = gParms->itemName;
  274.     ib.dirInfo.ioFVersNum = 0;
  275.     ib.dirInfo.ioFDirIndex = -1;    // Use DirID without name
  276.     if (PBGetCatInfoSync(&ib))
  277.         goto FatalError;
  278.     gParms->desDirID = ib.dirInfo.ioDrParID;
  279.     }
  280. return;
  281.  
  282. // Fatal error occurred
  283. FatalError:
  284. SysBeep(1);
  285. gParms->result = siHookQuit;    // Force installer to quit
  286. }
  287.  
  288.  
  289. /*****************************************************************************/
  290. Boolean GetFolder(
  291.         StringPtr prompt,        // Prompt string
  292.         short *foldVRefNum,    // Volume reference number of selected folder
  293.         long *foldDirID        // Directory ID of selected folder
  294.         )
  295. /******************************************************************************
  296.     Prompt the user to select a folder. Return TRUE if user cancels operation or
  297.     an error occurs. Otherwise, return FALSE and return the selected folder in
  298.     "foldVRefNum" and "foldDirID".
  299. ******************************************************************************/
  300. {
  301. Point sfWhere = { -1, -1 };
  302. FSSpec aliasFSSpec;
  303. Boolean targetIsFolder;
  304. Boolean wasAliased;
  305. CInfoPBRec ib;
  306.  
  307. if (gParms->groupEnvironFlags & siHookEnvSystem6OrLower)
  308.     {    // System 6 does not support auto-centering of dialog - set dialog location
  309.     sfWhere.h = 56;
  310.     sfWhere.v = 69;
  311.     }
  312.  
  313. gFolderSelected = false;
  314. gLastSFSaveDisk = 0;    // Force first update
  315. ParamText(prompt, gEmptyStr, gEmptyStr, gEmptyStr);
  316. SFPGetFile(sfWhere, gEmptyStr, AllFileFilterProc, -1, NULL, GetFolderDlgHookProc,
  317.                      &gSFReply, getFolderDlg, GetFolderDlgFilterProc);
  318. if (!gFolderSelected)
  319.     return true;    // User cancelled or an error occurred
  320.  
  321. if (gSFReply.fName[0])
  322.     {    // Filename in list selected - must be a folder alias or
  323.         // volume alias since we filtered out all normal files
  324.     // Resolve alias file
  325.     if (FSMakeFSSpec(-LMGetSFSaveDisk(), LMGetCurDirStore(), gSFReply.fName, &aliasFSSpec))
  326.         return true;    // Error - could not create FSSpec for alias file
  327.     if (ResolveAliasFile(&aliasFSSpec, true, &targetIsFolder, &wasAliased))
  328.         return true;    // Error - could not resolve alias
  329.     if (!targetIsFolder || !wasAliased)
  330.         return true;    // Error - invalid alias (shouldn't happen)
  331.  
  332.     ib.dirInfo.ioVRefNum = aliasFSSpec.vRefNum;
  333.     ib.dirInfo.ioDrDirID = aliasFSSpec.parID;
  334.     ib.dirInfo.ioNamePtr = aliasFSSpec.name;
  335.     ib.dirInfo.ioFVersNum = 0;
  336.     ib.dirInfo.ioFDirIndex = 0;    // Use DirID and name
  337.     if (PBGetCatInfoSync(&ib))
  338.         return true;    // Error
  339.     *foldDirID = ib.dirInfo.ioDrDirID;
  340.     *foldVRefNum = aliasFSSpec.vRefNum;
  341.     }
  342. else
  343.     {
  344.     *foldVRefNum = -LMGetSFSaveDisk();
  345.     if (gSFReply.fType != 0)
  346.         *foldDirID = gSFReply.fType;    // Folder in list selected
  347.     else
  348.         *foldDirID = LMGetCurDirStore();    // Nothing selected - use CurDirStore
  349.     }
  350. return false;
  351. }
  352.  
  353.  
  354. /*****************************************************************************/
  355. pascal Boolean GetFolderDlgFilterProc(
  356.         register DialogPtr theDialog,
  357.         EventRecord *theEvent,
  358.         short *itemHit
  359.         )
  360. /******************************************************************************
  361.     This filter proc is used with the GetFolder dialog. Enable/disable the new
  362.     OPEN button to match the state of the original OPEN button (a copy of the
  363.     button is used to eliminate the automatic bold outline).
  364. ******************************************************************************/
  365. {
  366. long holdA4;
  367. ControlHandle newOpenHdl;
  368. ControlHandle oldOpenHdl;
  369. ControlHandle selectHdl;
  370. Rect itemRect;
  371. short itemType;
  372. short length;
  373. short maxNameWidth;
  374. Str255 name;
  375. Str255 selectTitle;
  376. Str255 ellipsisStr;
  377. Str255 selectEndStr;
  378. short deskVRefNum;
  379. long deskDirID;
  380. CInfoPBRec ib;
  381. static long lastCurDirStore;
  382. static long lastFType;
  383. static Str255 lastName;
  384.  
  385. // Set up to access globals
  386. #ifdef THINK_C
  387. SetUpA4();
  388. #endif
  389. #ifdef __MWERKS__
  390. holdA4 = SetUpA4();
  391. #endif
  392.  
  393. // Set enabled state of NEW_OPEN button to be same as OLD_OPEN
  394. GetDialogItem(theDialog, newOpen_getFolderDlg, &itemType, (Handle *) &newOpenHdl, &itemRect);
  395. GetDialogItem(theDialog, oldOpen_getFolderDlg, &itemType, (Handle *) &oldOpenHdl, &itemRect);
  396. if ((*newOpenHdl)->contrlHilite != (*oldOpenHdl)->contrlHilite)
  397.     HiliteControl(newOpenHdl, (*oldOpenHdl)->contrlHilite);
  398.  
  399. if ((gLastSFSaveDisk != LMGetSFSaveDisk()) ||    (lastCurDirStore != LMGetCurDirStore())
  400.     || (lastFType != gSFReply.fType) || !EqualString(lastName, gSFReply.fName, true, true))
  401.     {    // Selection has changed - update SELECT button title
  402.     if (gSFReply.fName[0])
  403.         {    // Filename in list selected - must be a folder alias or
  404.             // volume alias since we filtered out all normal files
  405.         BlockMove(gSFReply.fName, &name, gSFReply.fName[0] + 1);
  406.         }
  407.     else
  408.         {
  409.         if (gSFReply.fType == 0)
  410.             {    // Nothing selected - use CurDirStore
  411.             ib.dirInfo.ioDrDirID = LMGetCurDirStore();
  412.             if (gParms->groupEnvironFlags & siHookEnvSystem7OrHigher)
  413.                 {    // Check for special case of desktop folder selected
  414.                 if (!FindFolder(-LMGetSFSaveDisk(), kDesktopFolderType, kDontCreateFolder, &deskVRefNum, &deskDirID))
  415.                     if (deskDirID == LMGetCurDirStore())
  416.                         {    // Desktop folder selected - use text string for title
  417.                         GetIndString(name, getFolderIStrID, desktopIStr);    // Get "Desktop"
  418.                         if (name[0] != 0)
  419.                             goto Name_Done;
  420.                         }
  421.                 }
  422.             }
  423.         else
  424.             {    // Folder in list selected
  425.             ib.dirInfo.ioDrDirID = gSFReply.fType;
  426.             }
  427.         ib.dirInfo.ioVRefNum = -LMGetSFSaveDisk();
  428.         ib.dirInfo.ioNamePtr = name;
  429.         ib.dirInfo.ioFVersNum = 0;
  430.         ib.dirInfo.ioFDirIndex = -1;    // Use DirID without name
  431.         if (PBGetCatInfoSync(&ib))
  432.             name[0] = 0;    // Clear name if error occurs
  433.         }
  434. Name_Done:
  435.  
  436.     // Set new title of SELECT button
  437.     GetIndString(selectTitle, getFolderIStrID, selectBeginIStr);    // Get "Select “"
  438.     GetIndString(selectEndStr, getFolderIStrID, selectEndIStr);        // Get "”"
  439.  
  440.     GetDialogItem(theDialog, select_getFolderDlg, &itemType, (Handle *) &selectHdl, &itemRect);
  441.     maxNameWidth = itemRect.right - itemRect.left - StringWidth(selectTitle) - StringWidth(selectEndStr) - 20;
  442.     length = TruncateLengthStr(name, maxNameWidth);    // Truncate name if necessary
  443.  
  444.     BlockMove(&name[1], &selectTitle[selectTitle[0] + 1], length);
  445.     selectTitle[0] += length;
  446.     if (length != name[0])
  447.         {    // Name was truncated - add truncation character(s)
  448.         GetIndString(ellipsisStr, getFolderIStrID, ellipsisIStr);    // Get "…" (may be multiple bytes)
  449.         BlockMove(&ellipsisStr[1], &selectTitle[selectTitle[0] + 1], ellipsisStr[0]);
  450.         selectTitle[0] += ellipsisStr[0];
  451.         }
  452.     BlockMove(&selectEndStr[1], &selectTitle[selectTitle[0] + 1], selectEndStr[0]);
  453.     selectTitle[0] += selectEndStr[0];
  454.     SetControlTitle(selectHdl, selectTitle);
  455.     
  456.     // Save current folder parameters for later comparison
  457.     gLastSFSaveDisk = LMGetSFSaveDisk();
  458.     lastCurDirStore = LMGetCurDirStore();
  459.     lastFType = gSFReply.fType;
  460.     BlockMove(gSFReply.fName, &lastName, gSFReply.fName[0] + 1);
  461.     }
  462. #ifdef THINK_C
  463. RestoreA4();
  464. #endif
  465. #ifdef __MWERKS__
  466. RestoreA4(holdA4);
  467. #endif
  468. return false;
  469. }
  470.  
  471.  
  472. /*****************************************************************************/
  473. pascal short GetFolderDlgHookProc(
  474.         short item,
  475.         DialogPtr theDialog
  476.         )
  477. /******************************************************************************
  478.     Process the SELECT and NEW_OPEN buttons in the Extract dialog.
  479. ******************************************************************************/
  480. {
  481. long holdA4;
  482. ControlHandle oldOpenHdl;
  483. Rect itemRect;
  484. short itemType;
  485. EventRecord anEvent;
  486.  
  487. // Set up to access globals
  488. #ifdef THINK_C
  489. SetUpA4();
  490. #endif
  491. #ifdef __MWERKS__
  492. holdA4 = SetUpA4();
  493. #endif
  494.  
  495. switch (item)
  496.     {
  497.     case select_getFolderDlg:
  498.         // SELECT button clicked - translate to CANCEL to terminate
  499.         gFolderSelected = true;
  500.         item = cancel_getFolderDlg;
  501.         break;
  502.  
  503.     case newOpen_getFolderDlg:
  504.         // NEW_OPEN button clicked - translate to OLD_OPEN
  505.         GetDialogItem(theDialog, oldOpen_getFolderDlg, &itemType, (Handle *) &oldOpenHdl, &itemRect);
  506.         if ((*oldOpenHdl)->contrlHilite == 0)    // Ensure OLD_OPEN is enabled first
  507.             {    // Folder or volume
  508.             if (gSFReply.fName[0] == 0)
  509.                 item = sfHookOpenFolder;    // Folder or volume
  510.             else
  511.                 {    // File, file alias or folder/volume alias
  512.                 // Assume it is a folder/volume alias since no files are displayed
  513.                 OSEventAvail(0, &anEvent);
  514.                 if (anEvent.modifiers & optionKey)
  515.                     item = sfHookGoToAliasTarget;    // Go to alias target if OPTION key
  516.                 else
  517.                     item = sfHookOpenAlias;    // Folder/volume alias
  518.                 }
  519.             }
  520.         break;
  521.  
  522.     case newFolder_getFolderDlg:    // NEW_FOLDER button - Create new folder
  523.         if (!CreateNewFolder())
  524.             item = sfHookRebuildList;
  525.         break;
  526.     }
  527. #ifdef THINK_C
  528. RestoreA4();
  529. #endif
  530. #ifdef __MWERKS__
  531. RestoreA4(holdA4);
  532. #endif
  533. return item;
  534. }
  535.  
  536.  
  537. /*****************************************************************************/
  538. pascal Boolean AllFileFilterProc(
  539.         CInfoPBPtr infoPtr    // Pointer to file info block
  540.         )
  541. /******************************************************************************
  542.     This procedure filters out all files.
  543. ******************************************************************************/
  544. {
  545. return true;
  546. }
  547.  
  548.  
  549. /*****************************************************************************/
  550. short TruncateLengthStr(
  551.         StringPtr theStr,    // String to truncate
  552.         short maxWidth        // Maximum width of string
  553.         )
  554. /******************************************************************************
  555.     Return the maximum number of characters which may be printed in the
  556.     specified width. Note that the graph port must be set with the appropriate
  557.     font info.
  558. ******************************************************************************/
  559. {
  560. short posList[256];
  561. short index;
  562. Str255 ellipsisStr;
  563.  
  564. MeasureText(theStr[0], &theStr[1], posList);
  565. index = theStr[0];
  566. if (posList[index] > maxWidth)
  567.     {    // Truncation required
  568.     GetIndString(ellipsisStr, getFolderIStrID, ellipsisIStr);
  569.     maxWidth -= StringWidth(ellipsisStr);    // Allow space for "…"
  570.     while (posList[--index] > maxWidth)
  571.         ;
  572.     }
  573. return index;
  574. }
  575.  
  576.  
  577. /*****************************************************************************/
  578. Boolean CreateNewFolder(void)
  579. /******************************************************************************
  580.     Display a dialog and allow the user to select a name for a new folder.
  581.     Create the folder in the directory specified by -SFSaveDisk and CurDirStore
  582.     and change CurDirStore to the new directory. Return FALSE if folder is
  583.     created successfully. Return TRUE if an error occurs or the user cancels the
  584.     operation.
  585. ******************************************************************************/
  586. {
  587. Str255 folderName;
  588. DialogPtr dlgPtr;
  589. short itemType;
  590. Handle itemHdl;
  591. Rect itemRect;
  592. short index;
  593. short item;
  594. Boolean nameIsBlank;
  595. long newDirID;
  596.  
  597. GetIndString(folderName, getFolderIStrID, newFolderTitleIStr);    // Get "untitled folder"
  598. dlgPtr = GetNewDialog(newFolderDlg, NULL, (WindowPtr) -1);
  599. if (!dlgPtr)
  600.     return true;
  601. GetDialogItem(dlgPtr, name_newFolderDlg, &itemType, &itemHdl, &itemRect);
  602. SetDialogItemText(itemHdl, folderName);
  603. SelectDialogItemText(dlgPtr, name_newFolderDlg, 0, 32767);    // Select folder name
  604. ShowWindow(dlgPtr);
  605. ModalDialog(NULL, &item);
  606. GetDialogItemText(itemHdl, folderName);
  607. DisposeDialog(dlgPtr);
  608. if (item != ok_newFolderDlg)
  609.     return true;    // Cancelled by user
  610. if (folderName[0] > 31)
  611.     folderName[0] = 31;    // Truncate if name is too long
  612.  
  613. // Replace all ':' characters with '-' and ensure there is at least one non-space
  614. // character in the name
  615. nameIsBlank = true;
  616. for (index = folderName[0]; index != 0; index--)
  617.     {
  618.     if ((folderName[index] == ':') && (CharByte((Ptr) &folderName[1], index - 1) == 0))
  619.         folderName[index] = '-';
  620.     if (folderName[index] != ' ')
  621.         nameIsBlank = false;
  622.     }
  623. if (nameIsBlank)
  624.     goto Error;
  625.  
  626. if (DirCreate(-LMGetSFSaveDisk(), LMGetCurDirStore(), folderName, &newDirID))
  627.     goto Error;
  628.  
  629. LMSetCurDirStore(newDirID);    // Move to new directory
  630. return false;    // Good completion
  631.  
  632. Error:    // Error occurred while creating directory
  633. CautionAlert(folderErrAlrt, NULL);
  634. return true;
  635. }
  636.